//
// Created by u on 23-7-21.
//

#ifndef CLOUD_CHINT_SRCS_XLOGGER_HPP_
#define CLOUD_CHINT_SRCS_XLOGGER_HPP_
#include <iostream>
#include <memory>
#include <string>
#include <sstream>

#include "spdlog/spdlog.h"
#include "spdlog/fmt/bin_to_hex.h"
#include "spdlog/fmt/ranges.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/async_logger.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/details/thread_pool.h"
#include "spdlog/details/thread_pool-inl.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/async.h"

enum LogMode {
	CONSOLE_MODE = 0x01,    		//控制台
	FILE_MODE = 0X02,    			//文件
	CONSOLE_AND_FILE_MODE = 0x03,   //控制台+文件
};

//日志名称
#define LOG_NAME "multi_sink"
//封装宏，没有该宏无法输出文件名、行号等信息
#define LOG_TRACE(...) SPDLOG_LOGGER_CALL(spdlog::get(LOG_NAME), spdlog::level::trace, __VA_ARGS__)
#define LOG_DEBUG(...) SPDLOG_LOGGER_CALL(spdlog::get(LOG_NAME), spdlog::level::debug, __VA_ARGS__)
#define LOG_INFO(...)  SPDLOG_LOGGER_CALL(spdlog::get(LOG_NAME), spdlog::level::info, __VA_ARGS__)
#define LOG_WARN(...)  SPDLOG_LOGGER_CALL(spdlog::get(LOG_NAME), spdlog::level::warn, __VA_ARGS__)
#define LOG_ERROR(...) SPDLOG_LOGGER_CALL(spdlog::get(LOG_NAME), spdlog::level::err, __VA_ARGS__)
#define LOG_CRITI(...) SPDLOG_LOGGER_CALL(spdlog::get(LOG_NAME), spdlog::level::critical, __VA_ARGS__)

class XLogger {
public:

	XLogger(const char *nFileName = nullptr,
		const int nMaxFileSize = 1024 * 1024 * 10,
		const int nMaxFile = 10,
		const bool bAsync = false,
		const LogMode logMode = CONSOLE_MODE,
		spdlog::level::level_enum logLevel = spdlog::level::trace)  {
		init(nFileName, nMaxFileSize, nMaxFile, bAsync, logMode, logLevel);
	}

	~XLogger() {
		if (init_) {
			this->deinit();
		}
	}

private:
	//初始化日志通道
	bool init(const char *nFileName,
		const int nMaxFileSize = 1024 * 1024 * 10,
		const int nMaxFile = 10,
		const bool bAsync = false,
		const LogMode logMode = CONSOLE_MODE,
		spdlog::level::level_enum logLevel = spdlog::level::trace) {
		if (init_) {
			printf("XLogger is already initialized\n");
			return false;
		}
		init_ = true;

		try {

#ifdef DEBUG
			const char *pFormat = "[%Y-%m-%d %H:%M:%S.%e] <thread %t> [%^%l%$] [%@,%!]\n%v";
#elif RELEASE
			const char *pFormat = "[%Y-%m-%d %H:%M:%S.%e] <thread %t> [%^%l%$] %v";
#else
			const char *pFormat = "[%Y-%m-%d %H:%M:%S.%e] <thread %t> [%^%l%$] %v";
#endif
			//sink容器
			std::vector<spdlog::sink_ptr> vecSink;

			//控制台
			if (logMode & CONSOLE_MODE) {
				auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
				console_sink->set_level(logLevel);
				console_sink->set_pattern(pFormat);
				vecSink.push_back(console_sink);
			}

			//文件
			if (logMode & FILE_MODE) {
				auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(nFileName, nMaxFileSize, nMaxFile);
				file_sink->set_level(logLevel);
				file_sink->set_pattern(pFormat);
				vecSink.push_back(file_sink);
			}

			//设置logger使用多个sink
			if (bAsync) //异步
			{
				spdlog::init_thread_pool(10240, 1);
				auto tp = spdlog::thread_pool();
				logger_ = std::make_shared<spdlog::async_logger>(LOG_NAME,
					begin(vecSink),
					end(vecSink),
					tp,
					spdlog::async_overflow_policy::block);
			} else { //同步
				logger_ = std::make_shared<spdlog::logger>(LOG_NAME, begin(vecSink), end(vecSink));
			}
			logger_->set_level(logLevel);

			//遇到warn级别，立即flush到文件
			logger_->flush_on(spdlog::level::warn);
			//定时flush到文件，每三秒刷新一次
			spdlog::flush_every(std::chrono::seconds(3));
			spdlog::register_logger(logger_);
		}
		catch (const spdlog::spdlog_ex &ex) {
			std::cout << "Log initialization failed: " << ex.what() << std::endl;
			return false;
		}
		return true;
	}
	void deinit() {
		spdlog::drop_all();
		spdlog::shutdown();
	}

private:
	std::shared_ptr<spdlog::logger> logger_;
	bool init_;
};

static XLogger xLogger;
#endif //CLOUD_CHINT_SRCS_XLOGGER_HPP_
